package cloneable;

import org.testng.Assert;
import org.testng.annotations.Test;

import java.util.HashMap;

/**
 * 深浅克隆的区别是：
 * 	1 我们这里说的浅拷贝是指我们拷贝出来的对象内部的引用类型变量和原来对象内部引用类型变量是同一引用（指向同一对象）。
 * 	但是我们拷贝出来的对象和新对象不是同一对象。
 * 	简单来说，新（拷贝产生）、旧（元对象）对象不同，但是内部如果有引用类型的变量，新、旧对象引用的都是同一引用。
 *
 * 	2 深拷贝：全部拷贝原对象的内容，包括内存的引用类型也进行拷贝
 *
 * 	Tips：不建议使用clone方法，因为clone的约定很脆弱，对象拷贝的更好的方法是提供一个拷贝构造器（copy constructor）或拷贝工厂（copy factory）
 * 	拷贝构造器只是一个构造器，它唯一的参数类型是包含该构造器的类，而拷贝工厂是类似于拷贝构造器的静态工厂：
 * 	但是数组除外。对于数组对象，还是使用 clone 方法，或 Arrays.copyOf()
 */
public class MyClone implements Cloneable {


    private final int a = 1;

    /**
     * 如果使用Map接口声明，那么调用clone会报错，因为此时调用的Object#clone()方法，而这个方法是protected的，不能在这里访问
     */
    private HashMap<Integer, Integer> map = new HashMap<>();

    private MyObject object = new MyObject("lk");

    public MyClone() {}

    public MyClone(HashMap<Integer, Integer> map, MyObject object) {
        this.map = map;
        this.object = object;
    }

    @Override
    public MyClone clone() throws CloneNotSupportedException{
        try {
            // 浅克隆，会新创建个对象，但是对象内部如果有引用会直接复制，不会新创建引用类型的对象了，
            return (MyClone) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }

    @Override
    public String toString() {
        return "MyCloneTest{" +
                "a=" + a +
                ", map=" + map +
                ", object=" + object +
                '}';
    }

    /**
     * 克隆构造器
     */
    public MyClone(MyClone myClone) {
        this.map = myClone.map;
        this.object = myClone.object;
    }

    /**
     * 克隆工厂
     */
    public static MyClone copyMyclone(MyClone myClone) {
        return new MyClone(myClone.map, myClone.object);
    }

    @Test
    public void t1() throws CloneNotSupportedException {
        MyClone original = new MyClone();
        original.map.put(1, 1);
        MyClone clone = original.clone();
        // clone.object = (MyObject) original.object.clone();
        // clone.map = (HashMap<Integer, Integer>) original.map.clone();

        // 通过克隆得到的对象是浅克隆，就是
        System.out.println(original == clone); // false
        System.out.println(clone);

        assert original.map == clone.map;
        assert original.map.equals(clone.map);
        original.map.put(2, 2);
        System.out.println(original.map.equals(clone.map));
        System.out.println(original.map.get(1));
        System.out.println(clone.map.get(1));
    }


    @Test
    public void t2() throws CloneNotSupportedException {
        MyClone original = new MyClone();
        original.map.put(1, 1);
        MyClone clone = original.clone();
        clone.object = (MyObject) original.object.clone();

        // map的克隆是浅克隆，会把original的map里面的元素也克隆了
        clone.map = (HashMap<Integer, Integer>) original.map.clone();
        System.out.println("clone: " + clone);

        // 克隆之后，两个map添加新元素就互不影响了
        clone.map.put(2, 2);
        original.map.put(3, 3);
        System.out.println(clone.map);
        System.out.println(original.map.toString());

        // 深克隆，就是对象里面的变量也是新的
//        assert original.object == clone.object == false;

        Assert.assertTrue(original.object != clone.object);

    }




}
